package im.composer.media.sound.codec.seek;

import java.io.File;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map.Entry;
import java.util.concurrent.Callable;

import org.tritonus.share.sampled.FloatInputStream;

import javazoom.jl.decoder.Bitstream;
import javazoom.jl.decoder.Decoder;
import javazoom.jl.decoder.Header;
import javazoom.jl.decoder.SampleBuffer;

public class LameSeekSupport extends AbstractSeekSupport {

	Bitstream bitstream;

	public LameSeekSupport(File file) {
		this(Paths.get(file.toURI()));
	}

	public LameSeekSupport(Path path) {
		super(path);
	}

	public void setBitStream(Bitstream bitstream) {
		this.bitstream = bitstream;
	}

	@Override
	public void seek(long sample_position) {
		if (state == SeekSupportState.UNSUPPORTED) {
			return;
		}
		Entry<Long, Long> entry = null;
		do {
			entry = pos_map.floorEntry(sample_position);
			if(entry == null && state == SeekSupportState.BUILDING){
				try {
					Thread.sleep(0);
				} catch (InterruptedException e) {
					continue;
				}
			}
		} while (entry == null);
		if (entry != null) {
			try {
				bitstream.position(entry.getValue());
				Field field = FloatInputStream.class.getDeclaredField("position");
				field.setAccessible(true);
				field.set(fis, entry.getKey());
				fis.skip(sample_position - entry.getKey());
			} catch (Exception e) {
			}
		}
	}

	@Override
	protected Callable<Void> buildWorkder() {
		return () -> {
			Bitstream bitstream = new Bitstream(Files.newByteChannel(path));
			Decoder decoder = new Decoder();
			Header h = bitstream.readFrame();
			int channels = h.mode() == Header.SINGLE_CHANNEL ? 1 : 2;
			long pos = 0;

			while (true) {
				if (h == null) {
					h = bitstream.readFrame();
				}
				if (h == null) {
					break;
				}
				SampleBuffer output = (SampleBuffer) decoder.decodeFrame(h, bitstream);
				short[] s_buf = output.getBuffer();
				pos_map.put(pos, bitstream.position());
				pos += s_buf.length / channels;
				bitstream.closeFrame();
				h = null;
				if (output.getBufferLength() < 1) {
					continue;
				}
			}
			state = SeekSupportState.SUPPORT;
			writeSeekIndex();
			return null;
		};
	}

}
